package org.jing.core.lang.data;

import org.jing.core.comp.Components;
import org.jing.core.lang.Const;
import org.jing.core.lang.EnumFactory;
import org.jing.core.lang.ExceptionHandler;
import org.jing.core.lang.JingException;
import org.jing.core.lang.annotation.SerializableField;
import org.jing.core.lang.annotation.SerializableType;
import org.jing.core.lang.data.json.*;
import org.jing.core.lang.itf.JEnum;
import org.jing.core.lang.itf.Transfer;
import org.jing.core.util.ClassUtil;
import org.jing.core.util.GenericUtil;
import org.jing.core.util.StringUtil;
import org.w3c.dom.*;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * Description: 数据载体. <br>
 * 可与序列化/Json/Xml/Map进行互相转换. <br>
 * 说明一些概念: <br>
 * <b>1. 路径</b>: <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;<u>路径以<b>'.'</b>分割, 在路径检索时, 只会依次分别取第一个匹配到的节点.
 * 比如现在有a->[b1->c1,b2-c2,b1-c3], 在检索a.b1.c3时并不会检测到最后一条路径, 而是直接视为未能找到该路径.
 * 因为路径<b>a</b>匹配到了[<b>a</b>-b1->c1], 子路径<b>b</b>匹配到了[<b>a->b1</b>->c1], 而子路径<b>c3</b>无法在现有路径中匹配到任何子节点, 故该路径无法被找到. </u><
 * 如果想要实现根据自己想要的路径去精准操作子节点, 需要使用getCarrier去一步步获取.
 * 这样设计的考虑一是代码设计上会简单很多, 二是足以满足大部分操作场景, 非常适合用于配置项单一化的场景, 比如.yml/.properties/.json. <br>
 * <b>2. 类型</b>: <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;<b>2.1. Array类型</b>: 列表对象, 诸如<b>[]</b> <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;<b>2.1. Object类型</b>: 独立对象, 诸如<b>Carrier</b>/<b>String</b>/<b>Number</b> <br>
 *
 * @author bks <br>
 * @since 2024-07-23 <br>
 */
@SuppressWarnings({"unused", "WeakerAccess", "UnusedReturnValue", "Duplicates", "SameReturnValue", "rawtypes", "ClassEscapesDefinedScope"})
public class Carrier {
    private volatile String key = "";

    private boolean isMultiply = false;

    private final List<Carrier> childList = new ArrayList<>();

    private volatile Object value = null;

    private LinkedHashMap<String, String> attrMap = null;

    private static Charset defaultXmlCharset = StandardCharsets.UTF_8;

    public static void setDefaultXmlCharset(Charset charset) {
        defaultXmlCharset = charset;
    }

    public Carrier() {
        key = Const.CARRIER_ROOT_NODE;
    }

    public Carrier(String key) {
        this.setKey(key);
        this.setValue(null);
    }

    public Carrier(String key, Object value) {
        this.setKey(key);
        this.setValue(value);
    }

    public Carrier(String key, Carrier node) {
        this.setKey(key);
        this.addCarrier(node);
    }

    public Carrier setKey(String key) {
        if (StringUtil.isEmpty(key)) throw new JingException("key required");
        this.key = key;
        return this;
    }

    public String getKey() {
        return key;
    }

    public Carrier setValue(Object value) {
        this.value = value;
        return this;
    }

    public Object getValue() {
        return this.value;
    }

    public Carrier setString(Object value) {
        return setValue(ClassUtil.cast2String(value));
    }

    public String getString() {
        return ClassUtil.cast2String(getValue());
    }

    public Carrier setNumber(Object value) {
        return setValue(ClassUtil.cast2Number(value));
    }

    public BigDecimal getNumber() {
        return ClassUtil.cast2Number(getValue());
    }

    public Carrier setBoolean(Object value) {
        return setValue(ClassUtil.cast2Boolean(value));
    }

    public boolean getBoolean() {
        return ClassUtil.cast2Boolean(getValue());
    }

    public boolean isMultiply() {
        return isMultiply;
    }

    public Carrier setMultiply(boolean multiply) {
        isMultiply = multiply;
        return this;
    }

    public boolean isActive() {
        return EmptyArrayNode.INSTANCE != getValue();
    }

    public boolean isActive(String key) {
        return EmptyArrayNode.INSTANCE != getValue() && getKey().equals(key);
    }

    private Pair2<String, Carrier> getPenultimateCarrierByPath(String path, boolean createIfNotExist) {
        String[] keys = path.split("\\.");
        int size = GenericUtil.count(keys);
        if (size == 0) throw new JingException("invalid path: [{}]", path);
        Carrier p = this, c;
        String key;
        boolean flag;
        for (int i$ = 0, l$ = size - 1; i$ < l$; i$ ++) {
            key = keys[i$];
            flag = false;
            for (Carrier c$ : p.childList) {
                if (key.equals(c$.getKey())) {
                    p = c$;
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                if (createIfNotExist) {
                    Carrier newNode = new Carrier(key);
                    p.addCarrier(newNode);
                    p = newNode;
                }
                else {
                    return null;
                }
            }
        }
        return new Pair2<>(keys[size - 1], p);
    }

    public Carrier addCarrier(Carrier child) {
        if (null == child) {
            throw new JingException("cannot add null carrier into child list");
        }
        this.childList.add(child);
        return this;
    }

    public Carrier addAll(Carrier carrier) {
        this.childList.addAll(carrier.childList);
        return this;
    }

    public Carrier putAll(Carrier carrier) {
        Set<String> removedSet = new HashSet<>();
        String key;
        for (int i$ = 0, l$ = GenericUtil.count(childList); i$ < l$; i$ ++) {
            key = childList.get(i$).getKey();
            if (!removedSet.contains(key) && carrier.getCount(key) > 0) {
                removedSet.add(key);
                remove(key);
                l$ = GenericUtil.count(childList);
                i$ -- ;
            }
        }
        addAll(carrier);
        removedSet.clear();
        return this;
    }

    public Carrier clear() {
        childList.clear();
        if (null != attrMap) {
            attrMap.clear();
        }
        return this;
    }

    public Carrier remove(int seq, String path) {
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, false);
        if (null != p) {
            String key = p.getA();
            List<Carrier> children = p.getB().getChildList();
            Carrier child;
            int index = -1;
            for (int i$ = 0, l$ = children.size(); i$ < l$; i$ ++) {
                if (key.equals(children.get(i$).getKey())) {
                    index ++;
                    if (seq < 0 || index == seq) {
                        children.remove(i$);
                        i$ --;
                        l$ --;
                    }
                }
            }
        }
        return this;
    }

    public Carrier remove(String path) {
        return remove(-1, path);
    }

    private Carrier addValueImpl(String path, Object value) {
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, true);
        assert p != null;
        String key = p.getA();
        Carrier carrier = p.getB();
        List<Carrier> children = carrier.getChildList();
        GenericUtil.loop(children, c$ -> {
            if (c$.isActive(key)) {
                c$.setMultiply(true);
            }
        });
        carrier.addCarrier(new Carrier(key, value).setMultiply(true));
        return this;
    }

    private Carrier setValueImpl(int seq, String path, Object value) {
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, true);
        assert p != null;
        String key = p.getA();
        Carrier carrier = p.getB();
        int count = -1;
        List<Carrier> children = carrier.getChildList();
        Carrier child;
        for (int i$ = 0, l$ = children.size(); i$ < l$; i$ ++) {
            child = children.get(i$);
            // 过滤删除掉null节点
            if (null == child) {
                children.remove(i$);
                i$ --;
                l$ --;
                continue;
            }
            // 匹配键
            if (child.getKey().equals(key)) {
                // seq < 0 时为设置模式, 此模式需清除所有同名子节点
                if (seq < 0) {
                    children.remove(i$);
                    i$ --;
                    l$ --;
                    continue;
                }
                else {
                    count ++;
                    // 如果索引不相等则证明该key会存在复数形式, 所以要变更为复数类型
                    if (count < seq) {
                        child.setMultiply(true);
                    }
                }
                if (count == seq) {
                    child.setValue(value);
                    return this;
                }
            }
        }
        // 没找到的情况下, 直接新增一个
        if (count == -1) {
            children.add(new Carrier(key, value));
            return this;
        }
        // 有搜寻到的时候, 采用add模式
        else {
            return addValueImpl(key, value);
        }
    }

    private Object getValueImpl(int seq, String path, Object defaultObject) {
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, false);
        Object retO = defaultObject;
        if (null != p) {
            String key = p.getA();
            Carrier carrier = p.getB();
            int count = -1;
            for (Carrier c$ : carrier.getChildList()) {
                if (c$.isActive(key)) {
                    count ++;
                    if (seq < 0 || seq == count) {
                        retO = c$.childList.isEmpty() ? c$.getValue() : c$;
                        break;
                    }
                }
            }
        }
        return retO;
    }

    public Carrier addString(String path, Object value) {
        return addValueImpl(path, ClassUtil.cast2String(value));
    }

    public Carrier setString(int seq, String path, Object value) {
        return setValueImpl(seq, path, ClassUtil.cast2String(value));
    }

    public Carrier setString(String path, Object value) {
        return setString(-1, path, value);
    }

    public String getString(int seq, String path, String defaultString) {
        return ClassUtil.cast2String(getValueImpl(seq, path, defaultString));
    }

    public String getString(int seq, String path) {
        return getString(seq, path, "");
    }

    public String getString(String path, String defaultString) {
        return getString(-1, path, defaultString);
    }

    public String getString(String path) {
        return getString(-1, path, "");
    }

    public Carrier addNumber(String path, Object value) {
        return addValueImpl(path, ClassUtil.cast2Number(value));
    }

    public Carrier setNumber(int seq, String path, Object value) {
        return setValueImpl(seq, path, ClassUtil.cast2Number(value));
    }

    public Carrier setNumber(String path, Object value) {
        return setNumber(-1, path, value);
    }

    public BigDecimal getNumber(int seq, String path, Object defaultNumber) {
        BigDecimal n1 = ClassUtil.cast2Number(getValueImpl(seq, path, null));
        BigDecimal n2 = ClassUtil.cast2Number(defaultNumber);
        return null == n1 ? n2 : n1;
    }

    public BigDecimal getNumber(int seq, String path) {
        return getNumber(seq, path, Const.NUM_0);
    }

    public BigDecimal getNumber(String path, Object defaultNumber) {
        return getNumber(-1, path, defaultNumber);
    }

    public BigDecimal getNumber(String path) {
        return getNumber(-1, path, Const.NUM_0);
    }

    public Carrier addBoolean(String path, Object value) {
        return addValueImpl(path, ClassUtil.cast2Boolean(value));
    }

    public Carrier setBoolean(int seq, String path, Object value) {
        return setValueImpl(seq, path, ClassUtil.cast2Boolean(value));
    }

    public Carrier setBoolean(String path, Object value) {
        return setBoolean(-1, path, value);
    }

    public boolean getBoolean(int seq, String path, boolean defaultBoolean) {
        Object value = getValueImpl(seq, path, null);
        return null == value ? defaultBoolean : ClassUtil.cast2Boolean(value);
    }

    public boolean getBoolean(int seq, String path) {
        return getBoolean(seq, path, false);
    }

    public boolean getBoolean(String path, boolean defaultBoolean) {
        return getBoolean(-1, path, defaultBoolean);
    }

    public boolean getBoolean(String path) {
        return getBoolean(-1, path, false);
    }

    public Carrier addValue(String path, Object value) {
        // 是字符串
        if (value instanceof String) {
            return addString(path, value);
        }
        // 是数字
        else if (ClassUtil.isNumber(value)) {
            return addNumber(path, value);
        }
        // 是布尔值
        else if (value instanceof Boolean) {
            return addBoolean(path, value);
        }
        else {
            return addValueImpl(path, value);
        }
    }

    public Carrier setValue(int seq, String path, Object value) {
        // 是字符串
        if (value instanceof String) {
            return setString(seq, path, value);
        }
        // 是数字
        else if (ClassUtil.isNumber(value)) {
            return setNumber(seq, path, value);
        }
        // 是布尔值
        else if (value instanceof Boolean) {
            return setBoolean(seq, path, value);
        }
        else {
            return setValueImpl(seq, path, value);
        }
    }

    public Carrier setValue(String path, Object value) {
        return setValue(-1, path, value);
    }

    public Object getValue(int seq, String path, Object defaultObject) {
        return getValueImpl(seq, path, defaultObject);
    }

    public Object getValue(int seq, String path) {
        return getValue(seq, path, null);
    }

    public Object getValue(String path, Object defaultObject) {
        return getValue(-1, path, defaultObject);
    }

    public Object getValue(String path) {
        return getValue(-1, path, null);
    }

    public Carrier attr(String name, String value) {
        if (null == attrMap) {
            attrMap = new LinkedHashMap<>();
        }
        this.attrMap.put(name, value);
        return this;
    }

    public String attr(String name) {
        return null == attrMap ? null : this.attrMap.get(name);
    }

    public List<Carrier> getChildList() {
        return childList;
    }

    public <T> Carrier addList(String key, List<T> list, Transfer<T, Carrier> transfer) {
        List<Carrier> tempList = new ArrayList<>();
        GenericUtil.loop(list, l$ -> {
            ExceptionHandler.publishIfMatch(null == l$, "null list entity");
            tempList.add(transfer.accept(l$));
        });
        return addList(key, tempList);
    }

    public <T> Carrier addList(String key, List<T> list) {
        int length = getCount(key);
        if (0 != length) {
            for (int i$ = 0; i$ < length; ++ i$) {
                getCarrier(i$, key).setMultiply(true);
            }
        }
        if (0 == length && 0 == GenericUtil.count(list)) {
            addCarrier(new Carrier(key).setValue(emptyArrayNode()).setMultiply(true));
            return this;
        }
        for (T t : list) {
            ExceptionHandler.publishIfMatch(null == t, "null list entity");
            if (ClassUtil.isBasicType(t.getClass())) {
                addValue(key, t);
            }
            else if (t instanceof Carrier) {
                addCarrier(((Carrier) t).setKey(key).setMultiply(true));
            }
            else if (t instanceof Map) {
                addCarrier(parseMap((Map) t).setKey(key).setMultiply(true));
            }
            else if (t instanceof BaseSerializableDto) {
                addCarrier(parseSerializableDto((BaseSerializableDto) t).setKey(key).setMultiply(true));
            }
            else {
                throw new JingException("invalid list entity type: [{}]", t.getClass());
            }
        }
        return this;
    }

    public void forEachChild(BiConsumer<Integer, Carrier> action) {
        GenericUtil.loop(getChildList().stream().filter(Carrier::isActive).toArray(), (i$, c$) -> action.accept(i$, (Carrier) c$));
    }

    public void forEachChild(Consumer<Carrier> action) {
        getChildList().stream().filter(Carrier::isActive).forEach(action);
    }

    public void forEachChild(String key, Consumer<Carrier> action) {
        GenericUtil.loop(getChildList().stream().filter(Carrier::isActive).filter(c$ -> c$.getKey().equals(key)).toArray(), c$ -> action.accept((Carrier) c$));
    }

    public void forEachChild(String key, BiConsumer<Integer, Carrier> action) {
        GenericUtil.loop(getChildList().stream().filter(Carrier::isActive).filter(c$ -> c$.getKey().equals(key)).toArray(), (i$, c$) -> action.accept(i$, (Carrier) c$));
    }

    public void sortChild(Comparator<Carrier> c) {
        childList.sort(c);
    }

    public List<Pair2<String, Object>> getValueChildList() {
        return getValueChildList("");
    }

    private List<Pair2<String, Object>> getValueChildList(String prefix) {
        List<Pair2<String, Object>> retList = new ArrayList<>();
        for (Carrier child : childList) {
            if (child.childList.isEmpty()) {
                retList.add(new Pair2<>(prefix + child.getKey(), child.getValue()));
            }
            else if (child.isActive()) {
                retList.addAll(child.getValueChildList(prefix + child.getKey() + "."));
            }
        }
        return retList;
    }

    public List<Carrier> getList(String path) {
        List<Carrier> retList = null;
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, false);
        if (null != p) {
            String key = p.getA();
            for (Carrier c$ : p.getB().getChildList()) {
                if (c$.isActive(key)) {
                    if (null == retList) {
                        retList = new ArrayList<>();
                    }
                    retList.add(c$);
                }
            }
        }
        return retList;
    }

    public Carrier getCarrier(int seq, String path) {
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, false);
        if (null != p) {
            String key = p.getA();
            int count = -1;
            for (Carrier c$ : p.getB().getChildList()) {
                if (c$.isActive(key)) {
                    count ++;
                    if (seq < 0 || count == seq) {
                        return c$;
                    }
                }
            }
        }
        return null;
    }

    public Carrier getCarrier(String path) {
        return getCarrier(-1, path);
    }

    public int getCount(String path) {
        int count = 0;
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, false);
        if (null != p) {
            String key = p.getA();
            for (Carrier c$ : p.getB().getChildList()) {
                if (c$.isActive(key)) count ++;
            }
        }
        return count;
    }

    public boolean contains(String path) {
        return 0 < getCount(path);
    }

    public boolean isNull() {
        return childList.isEmpty() && null == value;
    }

    public boolean isEmpty() {
        return childList.isEmpty() && (EmptyNode.INSTANCE == value || EmptyArrayNode.INSTANCE == value || value instanceof String && StringUtil.isEmpty((String) value));
    }

    public boolean isEmpty(String path) {
        Pair2<String, Carrier> p = getPenultimateCarrierByPath(path, false);
        boolean res = true;
        if (null != p) {
            for (Carrier c$ : p.getB().getChildList()) {
                res = res && c$.isEmpty();
            }
        }
        return res;
    }

    public String asXml() {
        return asXml(XmlFormat.FORMAT_NORMAL, 0);
    }

    public String asSimpleXml() {
        return asXml(XmlFormat.FORMAT_SIMPLE, 0);
    }

    public String asXml(XmlFormat format, int level) {
        return asXml(format, level, true);
    }

    private String asXml(XmlFormat format, int level, boolean first) {
        StringBuilder stbr = new StringBuilder();
        String key = getKey();
        String indent = StringUtil.repeat(format.getIndent(), level);
        if (first) {
            stbr.append(format.getHead());
        }
        else {
            stbr.append(indent);
        }
        stbr.append("<").append(key);
        if (null != attrMap && format.isNeedNodeAttr())  {
            for (Map.Entry<String, String> attr : attrMap.entrySet()) {
                validateAttr(attr);
                stbr.append(String.format(" %s=\"%s\"", attr.getKey(), StringUtil.ifEmpty(attr.getValue())));
            }
        }
        String content = getString();
        if (childList.isEmpty() && StringUtil.isEmpty(content)) {
            if (format.isExpandEmptyNode()) {
                stbr.append("></").append(key).append(">");
            }
            else {
                stbr.append("/>");
            }
        }
        else if (!childList.isEmpty()) {
            stbr.append(">");
            for (Carrier c$ : getChildList()) {
                if (!c$.isActive()) continue;
                stbr.append(format.getNewline()).append(c$.asXml(format, level + 1, false));
            }
            stbr.append(format.getNewline()).append(indent).append("</").append(key).append(">");
        }
        else {
            stbr.append(">").append(StringUtil.escape4XML(content)).append("</").append(key).append(">");
        }
        return stbr.toString();
    }

    public static Carrier parseXml(String xmlContent) {
        return parseXml(xmlContent, defaultXmlCharset);
    }

    public static Carrier parseXml(String xmlContent, Charset charset) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 禁止XML外部实体声明, 从而防止 XXE 攻击
            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            // 禁用加载外部 DTD
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            // 禁用外部通用实体
            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            // 禁用外部参数实体
            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            // 设置 XML 安全模式
            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            // 禁用命名空间处理中的外部实体解析
            factory.setFeature("http://xml.org/sax/features/namespaces", true);
            // 忽略文本节点上下空格
            factory.setIgnoringElementContentWhitespace(true);
            // 忽略注释
            factory.setIgnoringComments(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(xmlContent.getBytes(charset));
            Document document = builder.parse(stream);
            Element element = document.getDocumentElement();
            return parseXml(element);
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to parse by xml content");
        }
    }

    public static Carrier parseXml(Node node) {
        Carrier carrier = new Carrier(node.getNodeName());
        carrier.parseXml(node, 0);
        return carrier;
    }

    private boolean parseXml(Node node, int level) {
        NamedNodeMap attrMap = node.getAttributes();
        if (null != attrMap) {
            int size = attrMap.getLength();
            Node attrNode;
            for (int i$ = 0; i$ < size; i$++) {
                attrNode = attrMap.item(i$);
                this.attr(attrNode.getNodeName(), StringUtil.ifEmpty(attrNode.getNodeValue()));
            }
        }
        switch (node.getNodeType()) {
            case Node.ELEMENT_NODE: {
                NodeList nodeList = node.getChildNodes();
                int length = nodeList.getLength();
                if (length == 0) {
                    this.setValue("");
                }
                else {
                    Carrier childC;
                    Node childNode;
                    for (int i$ = 0; i$ < length; i$++) {
                        childNode = nodeList.item(i$);
                        if (childNode.getNodeType() == Node.TEXT_NODE) {
                            if (this.parseXml(childNode, level + 1)) {
                                return true;
                            }
                        }
                        else {
                            childC = new Carrier(childNode.getNodeName());
                            if (childC.parseXml(childNode, level + 1)) {
                                this.addCarrier(childC);
                            }
                        }
                    }
                }
                return true;
            }
            case Node.TEXT_NODE: {
                if (node.getPreviousSibling() == null && node.getNextSibling() == null) {
                    this.setValue(StringUtil.ifEmpty(node.getNodeValue()).trim());
                    return true;
                }
                break;
            }
            default:
                throw new JingException("invalid node type: {}", node.getNodeType());
        }
        return false;
    }

    public static Carrier parseProperties(String propertiesContent) {
        Properties properties = new Properties();
        InputStream inputStream = new ByteArrayInputStream(propertiesContent.getBytes());
        try (
            StringReader stringReader = new StringReader(propertiesContent)
        ) {
            properties.load(stringReader);
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to parse properties");
        }
        return parseMap(properties);
    }

    public static Carrier parseMap(Map map) {
        Carrier carrier = new Carrier();
        for (Object entry : map.entrySet()) {
            carrier.setValue(ClassUtil.cast2String(((Map.Entry) entry).getKey()), ((Map.Entry) entry).getValue());
        }
        return carrier;
    }

    public <T extends Map> T toMap(Class<T> mapType) {
        try {
            Map instance = mapType.newInstance();
            toMap(null, instance);
            return (T) instance;
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to transfer to Map");
        }
    }

    private void toMap(String prefix, Map map) {
        prefix = StringUtil.isEmpty(prefix) ? "" : prefix + ".";
        for (Carrier c$ : childList) {
            if (GenericUtil.count(c$.getChildList()) == 0) {
                map.put(prefix + c$.getKey(), c$.getValue());
            }
            else {
                c$.toMap(prefix + c$.getKey(), map);
            }
        }
    }

    public String asJson() {
        return asJson(JsonFormat.FORMAT_NORMAL);
    }

    public String asSimpleJson() {
        return asJson(JsonFormat.FORMAT_SIMPLE);
    }

    public String asJson(JsonFormat format) {
        if (childList.isEmpty()) {
            return "{}";
        }
        else {
            return asJson(format, 0);
        }
    }

    public String asJson(JsonFormat format, int level) {
        StringBuilder stbr = new StringBuilder("{");
        String indent = StringUtil.repeat(format.getIndent(), level);
        LinkedHashMap<String, Pair2<Boolean, List<Carrier>>> map = new LinkedHashMap<>();
        String key;
        Pair2<Boolean, List<Carrier>> p;
        for (Carrier c$ : getChildList()) {
            key = c$.getKey();
            p = map.computeIfAbsent(key, k -> new Pair2<>(false, new ArrayList<>()));
            p.setA(p.getA() || c$.isMultiply());
            if (c$.isActive()) {
                p.getB().add(c$);
            }
        }
        List<Carrier> children;
        boolean flag = false;
        for (Map.Entry<String, Pair2<Boolean, List<Carrier>>> e$ : map.entrySet()) {
            if (flag) {
                stbr.append(",");
                if (StringUtil.isEmpty(format.getNewline())) {
                    stbr.append(format.getSpace());
                }
            }
            else {
                flag = true;
            }
            key = e$.getKey();
            p = e$.getValue();
            children = p.getB();
            stbr
                .append(format.getNewline())
                .append(indent)
                .append(format.getIndent())
                .append("\"")
                .append(format.escape(key))
                .append("\":")
                .append(format.getSpace());
            // 是json-object
            if (!p.getA() && children.size() == 1) {
                children.get(0).asJson(format, level + 1, stbr);
            }
            // 是json-array
            else {
                boolean arrFlag = false;
                stbr.append("[");
                for (Carrier c$ : children) {
                    if (arrFlag) {
                        stbr.append(",");
                        if (StringUtil.isEmpty(format.getNewline())) {
                            stbr.append(format.getSpace());
                        }
                    }
                    else {
                        arrFlag = true;
                    }
                    stbr.append(format.getNewline()).append(indent).append(format.getIndent()).append(format.getIndent());
                    c$.asJson(format, level + 1, stbr);
                }
                if(arrFlag) {
                    stbr.append(format.getNewline()).append(indent).append(format.getIndent());
                }
                stbr.append("]");
            }
        }
        if (!map.isEmpty()) {
            stbr.append(format.getNewline()).append(indent);
        }
        stbr.append("}");
        return stbr.toString();
    }

    private void asJson(JsonFormat format, int level, StringBuilder stbr) {
        if (getChildList().isEmpty()) {
            Object value = getValue();
            if (null == value) {
                stbr.append("null");
            }
            else if (EmptyNode.INSTANCE == value) {
                stbr.append("{}");
            }
            else if (ClassUtil.isNumber(value) || value instanceof Boolean) {
                stbr.append(ClassUtil.cast2String(value));
            }
            else {
                String val = format.escape(ClassUtil.cast2String(value));
                if (null != val && format.isNeedTrim()) {
                    val = val.trim();
                }
                stbr.append("\"").append(val).append("\"");
            }
        }
        else {
            stbr.append(asJson(format, level + 1));
        }
    }

    public static Carrier parseJson(String jsonContent) {
        // 判断空字符串
        if (StringUtil.isEmpty(jsonContent)) {
            throw new JingException("json is empty");
        }
        jsonContent = jsonContent.trim();
        if (StringUtil.isEmpty(jsonContent)) {
            throw new JingException("json is empty");
        }
        Tokenizer tokenizer = new Tokenizer(new CharReader(new StringReader(jsonContent)));
        Token token = tokenizer.next();
        if (token.getType() == TokenType.BEGIN_OBJECT) {
            return parseJson(tokenizer, null);
        }
        else {
            throw new JingException("token '{' expected when json begin");
        }
    }

    public static Carrier parseJson(Tokenizer tokenizer, String rootName) {
        Carrier carrier = new Carrier(StringUtil.ifEmpty(rootName, Const.CARRIER_ROOT_NODE));
        int expectToken = TokenType.STRING.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
        String key = null;
        Object value;
        while (true) {
            Token token = tokenizer.next();
            TokenType tokenType = token.getType();
            String tokenValue = token.getValue();
            tokenType.checkExpectToken(expectToken);
            switch (tokenType) {
                case BEGIN_OBJECT:
                    carrier.addCarrier(parseJson(tokenizer, key));
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
                    break;
                case END_OBJECT:
                    if (null == carrier.getValue() && carrier.childList.isEmpty()) {
                        carrier.setValue(emptyNode());
                    }
                    return carrier;
                case BEGIN_ARRAY:
                    carrier.parseJsonArr(tokenizer, key);
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
                    break;
                case NULL:
                    carrier.setValue(key, null);
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
                    break;
                case NUMBER:
                    carrier.setNumber(key, tokenValue);
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
                    break;
                case BOOLEAN:
                    carrier.setBoolean(key, tokenValue);
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
                    break;
                case STRING:
                    Token preToken = tokenizer.previous();
                    if (preToken.getType() == TokenType.SEP_COLON) {
                        carrier.setString(key, tokenValue);
                        expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_OBJECT.getTokenCode();
                    } else {
                        key = tokenValue;
                        expectToken = TokenType.SEP_COLON.getTokenCode();
                    }
                    break;
                case SEP_COLON:
                    expectToken = TokenType.NULL.getTokenCode()
                        | TokenType.NUMBER.getTokenCode()
                        | TokenType.BOOLEAN.getTokenCode()
                        | TokenType.STRING.getTokenCode()
                        | TokenType.BEGIN_OBJECT.getTokenCode()
                        | TokenType.BEGIN_ARRAY.getTokenCode();
                    break;
                case SEP_COMMA:
                    expectToken = TokenType.STRING.getTokenCode();
                    break;
                case END_DOCUMENT:
                    throw new JingException("Unexpected end of document inside json");
                default:
                    throw new JingException("unexpected token: {}", tokenType);
            }
        }
    }

    private void parseJsonArr(Tokenizer tokenizer, String rootName) {
        int expectToken = TokenType.BEGIN_ARRAY.getTokenCode()
            | TokenType.END_ARRAY.getTokenCode()
            | TokenType.BEGIN_OBJECT.getTokenCode()
            | TokenType.NULL.getTokenCode()
            | TokenType.NUMBER.getTokenCode()
            | TokenType.BOOLEAN.getTokenCode()
            | TokenType.STRING.getTokenCode();
        rootName = StringUtil.ifEmpty(rootName, Const.CARRIER_ROOT_NODE);
        boolean emptyFlag = true;
        while (true) {
            Token token = tokenizer.next();
            TokenType tokenType = token.getType();
            String tokenValue = token.getValue();
            tokenType.checkExpectToken(expectToken);
            switch (tokenType) {
                case BEGIN_OBJECT:
                    addCarrier(parseJson(tokenizer, rootName).setMultiply(true));
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_ARRAY.getTokenCode();
                    break;
                case BEGIN_ARRAY:
                    parseJsonArr(tokenizer, rootName);
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_ARRAY.getTokenCode();
                    break;
                case END_ARRAY:
                case END_DOCUMENT:
                    if (emptyFlag) {
                        addCarrier(new Carrier(rootName).setValue(emptyArrayNode()).setMultiply(true));
                    }
                    return;
                case NULL:
                    addCarrier(new Carrier(rootName).setValue(null).setMultiply(true));
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_ARRAY.getTokenCode();
                    break;
                case NUMBER:
                    addCarrier(new Carrier(rootName).setNumber(tokenValue).setMultiply(true));
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_ARRAY.getTokenCode();
                    break;
                case BOOLEAN:
                    addCarrier(new Carrier(rootName).setBoolean(tokenValue).setMultiply(true));
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_ARRAY.getTokenCode();
                    break;
                case STRING:
                    addCarrier(new Carrier(rootName).setString(tokenValue).setMultiply(true));
                    expectToken = TokenType.SEP_COMMA.getTokenCode() | TokenType.END_ARRAY.getTokenCode();
                    break;
                case SEP_COMMA:
                    expectToken = TokenType.STRING.getTokenCode()
                        | TokenType.NULL.getTokenCode()
                        | TokenType.NUMBER.getTokenCode()
                        | TokenType.BOOLEAN.getTokenCode()
                        | TokenType.BEGIN_ARRAY.getTokenCode()
                        | TokenType.BEGIN_OBJECT.getTokenCode();
                    break;
                default:
                    throw new JingException("unexpected token: {}", tokenType);
            }
            emptyFlag = false;
        }
    }

    public static Carrier parseSerializableDto(BaseSerializableDto dto) {
        return parseSerializableDto(null, dto);
    }

    public static Carrier parseSerializableDto(String prefix, BaseSerializableDto dto) {
        SerializableType sType = ClassUtil.getAnnotationWithException(dto.getClass(), SerializableType.class);
        prefix = (StringUtil.isEmpty(prefix) ? "" : (prefix + "."));
        if (StringUtil.isNotEmpty(sType.value())) {
            prefix += sType.value() + ".";
        }
        Carrier retC = new Carrier();
        List<Field> fields = ClassUtil.getFields(dto.getClass());
        Field field;
        SerializableField sField;
        String path, nodeName, lastNodePath;
        Carrier lastNode, childC, tempC;
        Object value;
        Class fieldType;
        boolean exist;
        for (int i$ = 0, l$ = GenericUtil.count(fields); i$ < l$; i$ ++) {
            field = fields.get(i$);
            if (null == (sField = field.getAnnotation(SerializableField.class))) {
                continue;
            }
            path = prefix + StringUtil.ifEmpty(sField.value(), field.getName());
            if (path.contains(".")) {
                nodeName = path.substring(path.lastIndexOf(".") + 1);
                lastNodePath = path.substring(0, path.lastIndexOf("."));
                lastNode = null;
            }
            else {
                nodeName = path;
                lastNodePath = "";
                lastNode = retC;
            }
            value = ClassUtil.getFieldValue(dto, field);
            // 必填校验
            if (null == value) {
                ExceptionHandler.publishIfMatch(sField.required(), "[{}.{}] required", dto.getClass(), path);
                if (Void.class != sField.defaultValueType()) {
                    value = ClassUtil.cast(sField.defaultValueType(), sField.defaultValue());
                }
                else {
                    continue;
                }
            }
            fieldType = field.getType();
            boolean byFactory = false;
            if (DefaultSerializableFactory.class != sField.factory()) {
                value = Components.getInstance(sField.factory()).toCarrier(value, sField.factoryArgs());
                fieldType = null == value ? null: value.getClass();
                byFactory = true;
            }
            // 空
            if (null == fieldType) {
                retC.setValue(path, null);
            }
            // 基础类型
            else if (byFactory || ClassUtil.isBasicType(fieldType)) {
                retC.setValue(path, value);
            }
            // 枚举
            else if (JEnum.class.isAssignableFrom(fieldType)) {
                retC.setValue(path, ((JEnum) value).output());
            }
            // 是List
            else if (fieldType == List.class) {
                if (value instanceof String) {
                    retC.addValue(path, emptyArrayNode());
                }
                else {
                    int length = GenericUtil.count((List) value);
                    if (length == 0) {
                        retC.addValue(path, emptyArrayNode());
                    }
                    else {
                        Class entityType = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
                        for (int j$ = 0; j$ < length; j$ ++) {
                            if (ClassUtil.isBasicType(entityType)) {
                                retC.addValue(path, ((List) value).get(j$));
                            }
                            else if (BaseSerializableDto.class.isAssignableFrom(entityType) || Carrier.class == entityType) {
                                childC = Carrier.class == entityType ? (Carrier) ((List) value).get(j$) : Carrier.parseSerializableDto((BaseSerializableDto) ((List) value).get(j$));
                                childC.setMultiply(true);
                                if (null == lastNode) {
                                    if (retC.getCount(lastNodePath) == 0) {
                                        lastNode = retC;
                                        String[] nodeNames = lastNodePath.split("\\.");
                                        for (String n$ : nodeNames) {
                                            tempC = new Carrier(n$);
                                            lastNode.addCarrier(tempC);
                                            lastNode = tempC;
                                        }
                                    }
                                    else {
                                        lastNode = retC.getCarrier(lastNodePath);
                                    }
                                }
                                lastNode.addCarrier(childC.setKey(nodeName));
                            }
                            else {
                                throw new JingException("entity type {} must extend BaseSerializableDto", fieldType);
                            }
                        }
                    }
                }
            }
            // 是Carrier
            else if (fieldType == Carrier.class) {
                if (null == lastNode) {
                    if (retC.getCount(lastNodePath) == 0) {
                        lastNode = retC;
                        String[] nodeNames = lastNodePath.split("\\.");
                        for (String n$ : nodeNames) {
                            tempC = new Carrier(n$);
                            lastNode.addCarrier(tempC);
                            lastNode = tempC;
                        }
                    }
                    else {
                        lastNode = retC.getCarrier(lastNodePath);
                    }
                }
                lastNode.addCarrier(((Carrier) value).setKey(nodeName));
            }
            else if (BaseSerializableDto.class.isAssignableFrom(fieldType)) {
                childC = Carrier.parseSerializableDto((BaseSerializableDto) value);
                if (null == lastNode) {
                    if (retC.getCount(lastNodePath) == 0) {
                        lastNode = retC;
                        String[] nodeNames = lastNodePath.split("\\.");
                        for (String n$ : nodeNames) {
                            tempC = new Carrier(n$);
                            lastNode.addCarrier(tempC);
                            lastNode = tempC;
                        }
                    }
                    else {
                        lastNode = retC.getCarrier(lastNodePath);
                    }
                }
                lastNode.addCarrier(childC.setKey(nodeName));
            }
            else {
                throw new JingException("illegal type: {}", fieldType);
            }
        }
        return retC;
    }

    public <T extends BaseSerializableDto> T serialize(Class<T> type) {
        return serialize(null, type);
    }

    public <T extends BaseSerializableDto> T serialize(T instance) {
        return serialize(null, instance);
    }

    public <T extends BaseSerializableDto> T serialize(String prefix, Class<T> type) {
        return serialize(prefix, ClassUtil.createInstance(type));
    }

    public <T extends BaseSerializableDto> T serialize(String prefix, T instance) {
        Class<? extends BaseSerializableDto> type = instance.getClass();
        prefix = (StringUtil.isEmpty(prefix) ? "" : (prefix + "."));
        SerializableType sType = ClassUtil.getAnnotationWithException(type, SerializableType.class);
        if (StringUtil.isNotEmpty(sType.value())) {
            prefix += sType.value() + ".";
        }
        instance.before(this);
        List<Field> fields = ClassUtil.getFields(type);
        Field field;
        SerializableField sField;
        String path;
        Object value;
        Class fieldType;
        boolean exist;
        for (int i$ = 0, l$ = GenericUtil.count(fields); i$ < l$; i$ ++) {
            field = fields.get(i$);
            if (null == (sField = field.getAnnotation(SerializableField.class))) {
                continue;
            }
            path = prefix + StringUtil.ifEmpty(sField.value(), field.getName());
            fieldType = field.getType();
            exist = contains(path);
            // 必填校验1
            if (!exist) {
                ExceptionHandler.publishIfMatch(sField.required(), "path [{}] required", path);
                if (Void.class != sField.defaultValueType()) {
                    value = ClassUtil.cast(sField.defaultValueType(), sField.defaultValue());
                }
                else {
                    continue;
                }
            }
            else {
                value = getValue(path);
                // 必填校验2
                if (String.class == fieldType && (null == value || value instanceof String && StringUtil.isEmpty((String) value))) {
                    ExceptionHandler.publishIfMatch(sField.required(), "path [{}] required", path);
                    value = sField.defaultValue();
                }
            }
            if (DefaultSerializableFactory.class != sField.factory()) {
                value = Components.getInstance(sField.factory()).fromCarrier(value, sField.factoryArgs());
                fieldType = null == value ? null : value.getClass();
            }
            try {
                if (ClassUtil.cast(instance, field, value)) {
                    continue;
                }
            }
            catch (Throwable t) {
                throw new JingException(t, "failed to set instance {}.{}", type.getSimpleName(), field.getName());
            }
            // 为空
            if (null == fieldType) {
                try {
                    field.set(instance, null);
                }
                catch (Throwable t) {
                    throw new JingException(t, "failed to set null", field.getName());
                }
            }
            // 是特殊枚举
            else if (JEnum.class.isAssignableFrom(fieldType)) {
                try {
                    field.set(instance, EnumFactory.mapEnum(fieldType, value));
                }
                catch (Throwable t) {
                    throw new JingException(t, "illegal enum type or use: {}", fieldType);
                }
            }
            // 是List
            else if (fieldType == List.class) {
                Class entityType = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
                if (ClassUtil.isBasicType(entityType)) {
                    try {
                        List entities = new ArrayList<>();
                        field.set(instance, entities);
                        Carrier childC;
                        for (int j$ = 0, l$$ = this.getCount(path); j$ < l$$; j$ ++) {
                            childC = this.getCarrier(j$, path);
                            entities.add(ClassUtil.cast(entityType, childC.getString()));
                        }
                    }
                    catch (Throwable t) {
                        throw new JingException(t, "illegal list type or use: {}", fieldType);
                    }
                }
                else if (BaseSerializableDto.class.isAssignableFrom(entityType) || Carrier.class == entityType) {
                    try {
                        List entities = new ArrayList<>();
                        field.set(instance, entities);
                        Carrier childC;
                        for (int j$ = 0, l$$ = this.getCount(path); j$ < l$$; j$ ++) {
                            childC = this.getCarrier(j$, path);
                            entities.add(Carrier.class == entityType ? childC : childC.serialize(null, entityType));
                        }
                    }
                    catch (Throwable t) {
                        throw new JingException(t, "illegal list type or use: {}", fieldType);
                    }
                }
                else {
                    throw new JingException("entity type {} must extend BaseSerializableDto", fieldType);
                }
            }
            // 是Carrier
            else if (fieldType == Carrier.class) {
                try {
                    field.set(instance, this.getCarrier(path));
                }
                catch (Throwable t) {
                    throw new JingException(t, "failed to set Carrier", field.getName());
                }
            }
            else if (BaseSerializableDto.class.isAssignableFrom(fieldType)) {
                try {
                    field.set(instance, serialize(path, fieldType));
                }
                catch (Throwable t) {
                    throw new JingException(t, "failed to set DTO", fieldType);
                }
            }
            else {
                throw new JingException("illegal type: {}", fieldType);
            }
        }
        instance.after();
        return instance;
    }

    private void validateAttr(Map.Entry<String, String> attr) {
        if (!attr.getKey().matches("\\w+")) {
            throw new JingException("invalid attribute: {}=\"{}\"", attr.getKey(), attr.getValue());
        }
    }

    public static EmptyNode emptyNode() {
        return EmptyNode.INSTANCE;
    }

    public static EmptyArrayNode emptyArrayNode() {
        return EmptyArrayNode.INSTANCE;
    }

    public static JsonFormat buildJsonFormat() {
        return new JsonFormat();
    }

    public static XmlFormat buildXmlFormat() {
        return new XmlFormat();
    }

    @Override public String toString() {
        return asSimpleXml();
    }

    /**
     * Description: 空的json数组节点 <br>
     *
     * @author bks <br>
     * @since 2023-12-19 <br>
     */
    @SerializableType
    private static final class EmptyArrayNode extends BaseSerializableDto {
        private EmptyArrayNode() {}

        public static final EmptyArrayNode INSTANCE = new EmptyArrayNode();

        @Override public String toString() {
            return "";
        }
    }

    /**
     * Description: 空的json节点 <br>
     *
     * @author bks <br>
     * @since 2023-12-19 <br>
     */
    @SerializableType
    private static final class EmptyNode extends BaseSerializableDto {
        private EmptyNode() {}

        public static final EmptyNode INSTANCE = new EmptyNode();

        @Override public String toString() {
            return "";
        }
    }

    /**
     * Description: JSON输出格式, 注意的是其线程不安全, 避免定义成公用变量 <br>
     */
    public static class JsonFormat {
        /**
         * Description: 简版Json格式 <br>
         *
         * <b>缩进</b>: 关闭 <br>
         * <b>换行符</b>: 关闭 <br>
         * <b>分割空格</b>: 关闭 <br>
         * <b>trim</b>: 关闭 <br>
         */
        public static final JsonFormat FORMAT_SIMPLE = new JsonFormat().setIndent(false).setNewline(false).setSpace(false).lock();

        /**
         * Description: 通常Json格式 <br>
         *
         * <b>缩进</b>: 4个空格 <br>
         * <b>换行符</b>: \n <br>
         * <b>分割空格</b>: 1个空格 <br>
         * <b>trim</b>: 关闭 <br>
         */
        public static final JsonFormat FORMAT_NORMAL = new JsonFormat().lock();

        private boolean locked = false;

        private String newline = "\n";

        private String indent = "    ";

        private String space = " ";

        private boolean needTrim = false;

        private JsonFormat() {
        }

        /**
         * Description: 锁定格式, 不允许再修改. <br>
         * 可以将格式设为静态常量. <br>
         *
         * @return <u>链式调用</u> <br>
         */
        public synchronized JsonFormat lock() {
            this.locked = true;
            return this;
        }

        /**
         * Description: 检察格式是否已经锁定. <br>
         *
         * @throws JingException 格式已经锁定 <br>
         */
        private synchronized void checkLocked() {
            if (locked) throw new JingException("locked json format cannot be modified");
        }

        /**
         * Description: 设置换行字符串. <br>
         *
         * @param newline <b>'\n'</b>或者'\r\n', 必须条件是trim()后为空字符串 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setNewline(String newline) {
            checkLocked();
            this.newline = StringUtil.ifEmpty(newline);
            if (StringUtil.isNotEmpty(this.newline.trim())) throw new JingException("only \\n or \\r\\n can be set as new line");
            return this;
        }

        /**
         * Description: 设置是否启用默认换行字符串. <br>
         * 默认换行字符串为<b>'\n'</b> <br>
         *
         * @param needNewLine 是否需要启用默认换行字符串 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setNewline(boolean needNewLine) {
            return setNewline(needNewLine ? "\n" : "");
        }

        /**
         * Description: 获取换行字符串. <br>
         *
         * @return <br>
         */
        public String getNewline() {
            return newline;
        }

        /**
         * Description: 设置缩进需要的空格数量. <br>
         *
         * @param times 空格数量 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setIndent(int times) {
            checkLocked();
            this.indent = StringUtil.repeat(' ', times);
            return this;
        }

        /**
         * Description: 设置是否启用默认缩进空格数量. <br>
         * 默认缩进空格数量为<b>4</b>. <br>
         *
         * @param needIndent 是否启用默认缩进空格数量 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setIndent(boolean needIndent) {
            return setIndent(needIndent ? 4 : 0);
        }

        /**
         * Description: 获取缩进字符串. <br>
         *
         * @return <br>
         */
        public String getIndent() {
            return indent;
        }

        /**
         * Description: 设置在<b>','</b>后的分割空格数量. <br>
         *
         * @param times 分割空格数量 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setSpace(int times) {
            checkLocked();
            this.space = StringUtil.repeat(' ', times);
            return this;
        }

        /**
         * Description: 设置是否启用在<b>','</b>后的默认分割空格数量. <br>
         * 默认空格数量为<b>1</b>. <br>
         *
         * @param needSpace 需要启用默认分割空格数量 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setSpace(boolean needSpace) {
            return setSpace(needSpace ? 1 : 0);
        }

        /**
         * Description: 获取<b>','</b>后的分割空格数量. <br>
         *
         * @return <br>
         */
        public String getSpace() {
            return space;
        }

        /**
         * Description: 设置是否需要对键/值进行trim操作. <br>
         *
         * @param needTrim 是否需要对键/值进行trim操作 <br>
         * @return <u>链式调用</u> <br>
         */
        public JsonFormat setNeedTrim(boolean needTrim) {
            checkLocked();
            this.needTrim = needTrim;
            return this;
        }

        /**
         * Description: 获取是否需要对键/值进行trim操作. <br>
         *
         * @return  <br>
         */
        public boolean isNeedTrim() {
            return needTrim;
        }

        /**
         * Description: 转义json字符串. <br>
         *
         * @see StringUtil#escape4Json(String)
         * @param content 字符串 <br>
         * @return <br>
         */
        public String escape(String content) {
            return StringUtil.escape4Json(content);
        }

        /**
         * Description: 反转义json字符串. <br>
         *
         * @see StringUtil#unescape4Json(String)
         * @param content 字符串 <br>
         * @return <br>
         */
        public String unescape(String content) {
            return StringUtil.unescape4Json(content);
        }
    }

    public static class XmlFormat {
        public static final XmlFormat FORMAT_NORMAL = new XmlFormat().lock();

        public static final XmlFormat FORMAT_SIMPLE = new XmlFormat().setNewline(false).setIndent(false).setExpandEmptyNode(true).setNeedNodeAttr(false).setNeedHead(false).lock();

        private boolean locked = false;

        private String newline = "\n";

        private String indent = "    ";

        private boolean expandEmptyNode = false;

        private boolean needHead = false;

        private String headVersion = "1.0";

        private String headEncoding = "utf-8";

        private boolean needNodeAttr = true;

        private LinkedHashMap<String, String> headAttrMap = null;

        private XmlFormat() {}

        /**
         * Description: 锁定格式, 不允许再修改. <br>
         * 可以将格式设为静态常量. <br>
         *
         * @return <u>链式调用</u> <br>
         */
        public synchronized XmlFormat lock() {
            this.locked = true;
            return this;
        }

        /**
         * Description: 检察格式是否已经锁定. <br>
         *
         * @throws JingException 格式已经锁定 <br>
         */
        private synchronized void checkLocked() {
            if (locked) throw new JingException("locked xml format cannot be modified");
        }

        /**
         * Description: 设置换行字符串. <br>
         *
         * @param newline <b>'\n'</b>或者'\r\n', 必须条件是trim()后为空字符串 <br>
         * @return <u>链式调用</u> <br>
         */
        public XmlFormat setNewline(String newline) {
            checkLocked();
            this.newline = StringUtil.ifEmpty(newline);
            if (StringUtil.isNotEmpty(this.newline.trim())) throw new JingException("only \\n or \\r\\n can be set as new line");
            return this;
        }

        /**
         * Description: 设置是否启用默认换行字符串. <br>
         * 默认换行字符串为<b>'\n'</b> <br>
         *
         * @param needNewLine 是否需要启用默认换行字符串 <br>
         * @return <u>链式调用</u> <br>
         */
        public XmlFormat setNewline(boolean needNewLine) {
            return setNewline(needNewLine ? "\n" : "");
        }

        /**
         * Description: 获取换行字符串. <br>
         *
         * @return <br>
         */
        public String getNewline() {
            return newline;
        }

        /**
         * Description: 设置缩进需要的空格数量. <br>
         *
         * @param times 空格数量 <br>
         * @return <u>链式调用</u> <br>
         */
        public XmlFormat setIndent(int times) {
            checkLocked();
            this.indent = StringUtil.repeat(' ', times);
            return this;
        }

        /**
         * Description: 设置是否启用默认缩进空格数量. <br>
         * 默认缩进空格数量为<b>4</b>. <br>
         *
         * @param needIndent 是否启用默认缩进空格数量 <br>
         * @return <u>链式调用</u> <br>
         */
        public XmlFormat setIndent(boolean needIndent) {
            return setIndent(needIndent ? 4 : 0);
        }

        /**
         * Description: 获取缩进字符串. <br>
         *
         * @return <br>
         */
        public String getIndent() {
            return indent;
        }

        public XmlFormat setExpandEmptyNode(boolean expandEmptyNode) {
            checkLocked();
            this.expandEmptyNode = expandEmptyNode;
            return this;
        }

        public boolean isExpandEmptyNode() {
            return expandEmptyNode;
        }

        public String getHeadVersion() {
            return headVersion;
        }

        public XmlFormat setHeadVersion(String headVersion) {
            checkLocked();
            this.headVersion = headVersion;
            return this;
        }

        public String getHeadEncoding() {
            return headEncoding;
        }

        public XmlFormat setHeadEncoding(String headEncoding) {
            checkLocked();
            this.headEncoding = headEncoding;
            return this;
        }

        public XmlFormat headAttr(String name, String value) {
            checkLocked();
            if (null == headAttrMap) {
                headAttrMap = new LinkedHashMap<>();
            }
            headAttrMap.put(name, value);
            return this;
        }

        public String headAttr(String name) {
            if (null == headAttrMap) {
                return "";
            }
            return StringUtil.getMapString(headAttrMap, name);
        }

        public XmlFormat setNeedHead(boolean needHead) {
            checkLocked();
            this.needHead = needHead;
            return this;
        }

        public String getHead() {
            if (!needHead) {
                return "";
            }
            StringBuilder stbr = new StringBuilder();
            stbr.append("<?xml version=\"").append(headVersion).append("\"").append(" encoding=\"").append(headEncoding).append("\"");
            if (null != headAttrMap) {
                for (Map.Entry<String, String> attr : headAttrMap.entrySet()) {
                    stbr.append(" ").append(attr.getKey()).append("=\"").append(StringUtil.ifEmpty(attr.getValue())).append("\"");
                }
            }
            stbr.append("?>").append(getNewline()).append(getNewline());
            return stbr.toString();
        }

        public boolean isNeedNodeAttr() {
            return needNodeAttr;
        }

        public XmlFormat setNeedNodeAttr(boolean needNodeAttr) {
            checkLocked();
            this.needNodeAttr = needNodeAttr;
            return this;
        }
    }
}
